package com.ruoyi.framework.security.filter;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import com.ruoyi.framework.security.handle.DecryptionRequestWrapper;
import com.ruoyi.framework.security.handle.EncryptionResponseWrapper;
import org.bouncycastle.util.encoders.Hex;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 接口加密解密过滤器
 */
public class DecryptionFilter implements Filter {

    private Boolean enabled;
    private String key;
    private List<String> filterUrls;

    public DecryptionFilter(Boolean enabled, String key, List<String> filterUrls) {
        this.enabled = enabled;
        this.key = key;
        this.filterUrls = filterUrls;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public List<String> getFilterUrls() {
        return filterUrls;
    }

    public void setFilterUrls(List<String> filterUrls) {
        this.filterUrls = filterUrls;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 获取请求的 URL
        String requestUrl = httpRequest.getRequestURI();

        // 检查请求的 URL 是否匹配过滤列表中的规则
        if (!enabled||isUrlMatchFilterUrls(requestUrl)) {
            // 如果匹配上，直接放行，不走加密解密逻辑
            chain.doFilter(request, response);
            return;
        }

        // 缓存请求体以便多次读取
        String body = getRequestBody(httpRequest);
        // hutool国密对称加密算法SM4实现
        SM4 sm4 = SmUtil.sm4(Hex.decode(key));

        if (!body.isEmpty()) {
            body = body.replace("\"","");
            // 假设body中的双引号已被去除或其他预处理
            String decryptedBody = sm4.decryptStr(body);

            // 将解密后的请求体传入新的请求对象
            DecryptionRequestWrapper wrappedRequest = new DecryptionRequestWrapper(httpRequest, decryptedBody);

            // 使用自定义的HttpServletResponseWrapper来捕获响应体
            EncryptionResponseWrapper wrappedResponse = new EncryptionResponseWrapper(httpResponse);

            // 传递包装后的请求和响应对象
            chain.doFilter(wrappedRequest, wrappedResponse);

            // 在这里加密响应体
            byte[] originalContent = wrappedResponse.getContentAsByteArray();
            if (originalContent.length > 0) {
                String originalContentStr = new String(originalContent, StandardCharsets.UTF_8);
                String encryptedContent = sm4.encryptHex(originalContentStr);

                // 将加密后的响应写回给客户端
                httpResponse.getOutputStream().write(encryptedContent.getBytes(StandardCharsets.UTF_8));
                httpResponse.getOutputStream().flush();
            }
        } else {
            // 使用自定义的HttpServletResponseWrapper来捕获响应体
            EncryptionResponseWrapper wrappedResponse = new EncryptionResponseWrapper(httpResponse);

            // 传递包装后的请求和响应对象
            chain.doFilter(request, wrappedResponse);

            // 在这里加密响应体
            byte[] originalContent = wrappedResponse.getContentAsByteArray();
            if (originalContent.length > 0) {
                String originalContentStr = new String(originalContent, StandardCharsets.UTF_8);
                String encryptedContent = sm4.encryptHex(originalContentStr);

                // 将加密后的响应写回给客户端
                httpResponse.getOutputStream().write(encryptedContent.getBytes(StandardCharsets.UTF_8));
                httpResponse.getOutputStream().flush();
            }
        }
    }

    private boolean isUrlMatchFilterUrls(String requestUrl) {
        if (filterUrls == null) {
            return false;
        }
        for (String filterUrl : filterUrls) {
            if (isUrlMatchPattern(requestUrl, filterUrl)) {
                return true;
            }
        }
        return false;
    }

    private boolean isUrlMatchPattern(String requestUrl, String pattern) {
        String[] requestSegments = requestUrl.split("/");
        String[] patternSegments = pattern.split("/");

        int requestIndex = 0;
        int patternIndex = 0;

        while (requestIndex < requestSegments.length && patternIndex < patternSegments.length) {
            if ("*".equals(patternSegments[patternIndex])) {
                patternIndex++;
                // 处理连续的 * 通配符
                while (patternIndex < patternSegments.length && "*".equals(patternSegments[patternIndex])) {
                    patternIndex++;
                }
                if (patternIndex == patternSegments.length) {
                    // 如果模式的最后是 *，则直接匹配成功
                    return true;
                }
                // 查找下一个匹配的路径段
                while (requestIndex < requestSegments.length && !requestSegments[requestIndex].equals(patternSegments[patternIndex])) {
                    requestIndex++;
                }
                if (requestIndex == requestSegments.length) {
                    // 没有找到匹配的路径段
                    return false;
                }
            } else if (!requestSegments[requestIndex].equals(patternSegments[patternIndex])) {
                return false;
            }
            requestIndex++;
            patternIndex++;
        }

        // 检查是否所有路径段都匹配
        return requestIndex == requestSegments.length && patternIndex == patternSegments.length;
    }

    private String getRequestBody(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = request.getReader();
            char[] charBuffer = new char[128];
            int bytesRead;
            while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {
                stringBuilder.append(charBuffer, 0, bytesRead);
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    // 忽略
                }
            }
        }
        return stringBuilder.toString();
    }

    @Override
    public void destroy() {}
}